#region Copyright (c) 2006-2011 Widgetsphere LLC, All Rights Reserved
//--------------------------------------------------------------------- *
//                          Widgetsphere  LLC                           *
//             Copyright (c) 2006-2011 All Rights reserved              *
//                                                                      *
//                                                                      *
//This file and its contents are protected by United States and         *
//International copyright laws.  Unauthorized reproduction and/or       *
//distribution of all or any portion of the code contained herein       *
//is strictly prohibited and will result in severe civil and criminal   *
//penalties.  Any violations of this copyright will be prosecuted       *
//to the fullest extent possible under law.                             *
//                                                                      *
//THE SOURCE CODE CONTAINED HEREIN AND IN RELATED FILES IS PROVIDED     *
//TO THE REGISTERED DEVELOPER FOR THE PURPOSES OF EDUCATION AND         *
//TROUBLESHOOTING. UNDER NO CIRCUMSTANCES MAY ANY PORTION OF THE SOURCE *
//CODE BE DISTRIBUTED, DISCLOSED OR OTHERWISE MADE AVAILABLE TO ANY     *
//THIRD PARTY WITHOUT THE EXPRESS WRITTEN CONSENT OF WIDGETSPHERE LLC   *
//                                                                      *
//UNDER NO CIRCUMSTANCES MAY THE SOURCE CODE BE USED IN WHOLE OR IN     *
//PART, AS THE BASIS FOR CREATING A PRODUCT THAT PROVIDES THE SAME, OR  *
//SUBSTANTIALLY THE SAME, FUNCTIONALITY AS ANY WIDGETSPHERE PRODUCT.    *
//                                                                      *
//THE REGISTERED DEVELOPER ACKNOWLEDGES THAT THIS SOURCE CODE           *
//CONTAINS VALUABLE AND PROPRIETARY TRADE SECRETS OF WIDGETSPHERE,      *
//INC.  THE REGISTERED DEVELOPER AGREES TO EXPEND EVERY EFFORT TO       *
//INSURE ITS CONFIDENTIALITY.                                           *
//                                                                      *
//THE END USER LICENSE AGREEMENT (EULA) ACCOMPANYING THE PRODUCT        *
//PERMITS THE REGISTERED DEVELOPER TO REDISTRIBUTE THE PRODUCT IN       *
//EXECUTABLE FORM ONLY IN SUPPORT OF APPLICATIONS WRITTEN USING         *
//THE PRODUCT.  IT DOES NOT PROVIDE ANY RIGHTS REGARDING THE            *
//SOURCE CODE CONTAINED HEREIN.                                         *
//                                                                      *
//THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE.              *
//--------------------------------------------------------------------- *
#endregion
using System;
using System.Linq;
using Widgetsphere.Generator.Common.GeneratorFramework;
using Widgetsphere.Generator.Models;
using System.Text;
using Widgetsphere.Generator.Common.Util;
using System.Collections.Generic;

namespace Widgetsphere.Generator.NHibernateStandard.Generators.Mappings
{
	public class MappingExtenderTemplate : NHibernateBaseTemplate
	{
		private StringBuilder sb = new StringBuilder();
		private Table _currentTable = null;

		public MappingExtenderTemplate(ModelRoot model, Table table)
		{
			_model = model;
			_currentTable = table;
		}

		#region BaseClassTemplate overrides
		public override string FileName
		{
			get { return string.Format("{0}.hbm.xml", _currentTable.PascalName); }
		}

		public override string FileContent
		{
			get
			{
				try
				{
					GenerateContent();
					return sb.ToString();
				}
				catch (Exception ex)
				{
					throw;
				}
			}
		}

		#endregion

		#region GenerateContent

		public void GenerateContent()
		{
			try
			{
				sb.AppendLine("<hibernate-mapping xmlns=\"urn:nhibernate-mapping-2.2\" default-access=\"property\" auto-import=\"true\" default-cascade=\"none\" default-lazy=\"true\">");
				sb.AppendLine("	<class xmlns=\"urn:nhibernate-mapping-2.2\" " +
					"mutable=\"" + (!_currentTable.Immutable).ToString().ToLower() + "\" " +
					"name=\"" + this.DefaultNamespace + ".DataTransfer." + _currentTable.PascalName + ", " + this.DefaultNamespace + ".DataTransfer\" " +
					"table=\"" + _currentTable.DatabaseName + "\" " +
					">");
				

				//Primary Key
				var pkList = _currentTable.GeneratedColumns.Where(x => x.PrimaryKey).OrderBy(x => x.PascalName);
				if (pkList.Count() == 1)
				{
					var column = pkList.First();
					sb.AppendLine("		<id name=\"" + column.PascalName + "\">");
					sb.AppendLine("			<column name=\"" + column.DatabaseName + "\" />");
					if (column.Identity == IdentityTypeConstants.Database)
						sb.AppendLine("			<generator class=\"identity\" />");
					else
						sb.AppendLine("			<generator class=\"assigned\" />");

					sb.AppendLine("		</id>");
					sb.AppendLine();
				}
				else
				{
					sb.AppendLine("		<composite-id>");
					foreach (Column column in pkList)
					{
						sb.AppendLine("			<key-property name=\"" + column.PascalName + "\" column=\"" + column.DatabaseName + "\" />");
					}
					sb.AppendLine("		</composite-id>");
					sb.AppendLine();
				}

				//Relations
				foreach (Relation relation in _currentTable.GetRelations())
				{
					if (!relation.ParentTable.Generated || !relation.ChildTable.Generated)
					{
						//Do Nothing
					}
					else
					{
						if (relation.IsOneToOne)
						{
							//TODO
						}
						else if (relation.IsManyToMany)
						{
							var secondaryTable = relation.GetSecondaryAssociativeTable();
							sb.AppendLine("		<bag cascade=\"save-update\" name=\"" + secondaryTable.PascalName + "List\" table=\"" + relation.ChildTable.DatabaseName + "\" mutable=\"true\">");

							var pkColumn = relation.ParentTable.PrimaryKeyColumns.First();
							sb.AppendLine("			<key property-ref=\"" + pkColumn.PascalName + "\">");
							sb.AppendLine("				<column name=\"" + pkColumn.PascalName + "\" />");
							sb.AppendLine("			</key>");

							var fkColumn = secondaryTable.PrimaryKeyColumns.First();
							sb.AppendLine("			<many-to-many class=\"" + this.DefaultNamespace + ".DataTransfer." + secondaryTable.PascalName + "\">");
							sb.AppendLine("				<column name=\"" + fkColumn.DatabaseName + "\" />");
							sb.AppendLine("			</many-to-many>");
							sb.AppendLine("		</bag>");
							sb.AppendLine();
						}
						else
						{
							sb.AppendLine("		<bag name=\"" + relation.PascalRoleName + relation.ChildTable.PascalName + "List\" table=\"" + relation.ChildTable.DatabaseName + "\" mutable=\"true\" inverse=\"true\">");

							foreach (ColumnRelationship columnRelationship in relation.ColumnRelationships)
							{
								var parentColumn = columnRelationship.ParentColumnRef.Object as Column;
								var childColumn = columnRelationship.ChildColumnRef.Object as Column;

								sb.AppendLine("			<key property-ref=\"" + parentColumn.PascalName + "\">");
								sb.AppendLine("				<column name=\"" + childColumn.DatabaseName + "\" />");
								sb.AppendLine("			</key>");
							}

							sb.AppendLine("			<one-to-many class=\"" + this.DefaultNamespace + ".DataTransfer." + relation.ChildTable.PascalName + "\" />");
							sb.AppendLine("		</bag>");
							sb.AppendLine();
						}
					}
				}

				//Relation where I am CHILD
				foreach (Relation relation in _currentTable.GetRelationsWhereChild())
				{
					if (!relation.ParentTable.Generated || !relation.ChildTable.Generated)
					{
						//Do Nothing
					}
					else
					{
						if (relation.IsOneToOne)
						{
							//TODO
						}
						else //if (!relation.ParentTable.IsTypeTable)
						{
							sb.AppendLine("		<many-to-one name=\"" + relation.PascalRoleName + relation.ParentTable.PascalName + "\" class=\"" + this.DefaultNamespace + ".DataTransfer." + relation.ParentTable.PascalName + "\">");

							foreach (ColumnRelationship columnRelationship in relation.ColumnRelationships)
							{
								var parentColumn = columnRelationship.ParentColumnRef.Object as Column;
								var childColumn = columnRelationship.ChildColumnRef.Object as Column;

								sb.AppendLine("			<column name=\"" + parentColumn.DatabaseName + "\" not-null=\"" + ((!parentColumn.AllowNull).ToString().ToLower()) + "\" />");
							}

							sb.AppendLine("		</many-to-one>");
							sb.AppendLine();
						}
					}
				}

				//All other columns
				foreach (Column column in _currentTable.GeneratedColumns.Where(x => !x.PrimaryKey).OrderBy(x => x.PascalName))
				{
					sb.AppendLine("		<property name=\"" + column.PascalName + "\">");
					sb.AppendLine("			<column name=\"" + column.DatabaseName + "\" not-null=\"" + (!column.AllowNull).ToString().ToLower() + "\" />");
					sb.AppendLine("		</property>");
				}

				//Create Audit
				if (_currentTable.AllowCreateAudit)
				{
					sb.AppendLine("		<property name=\"" + _model.Database.CreatedByPascalName + "\">");
					sb.AppendLine("			<column name=\"" + _model.Database.CreatedByDatabaseName + "\" not-null=\"false\" />");
					sb.AppendLine("		</property>");
					sb.AppendLine("		<property name=\"" + _model.Database.CreatedDatePascalName + "\">");
					sb.AppendLine("			<column name=\"" + _model.Database.CreatedDateDatabaseName + "\" not-null=\"false\" />");
					sb.AppendLine("		</property>");
				}

				//Modified Audit
				if (_currentTable.AllowModifiedAudit)
				{
					sb.AppendLine("		<property name=\"" + _model.Database.ModifiedByPascalName + "\">");
					sb.AppendLine("			<column name=\"" + _model.Database.ModifiedByDatabaseName + "\" not-null=\"false\" />");
					sb.AppendLine("		</property>");
				}
				sb.AppendLine();

				#region Insert Stored Procedure
				//sb.AppendLine("		<sql-query name=\"gen_" + _currentTable.PascalName + "Insert\">");
				//sb.Append("			exec gen_" + _currentTable.PascalName + "Insert: ");
				//var columnList = _currentTable.GetColumnsFullHierarchy().Where(x => x.Generated && !x.ComputedColumn).OrderBy(x => x.Name).AsEnumerable().Select(x => x.PascalName).ToList();
				//if (_currentTable.AllowCreateAudit) columnList.Add(_model.Database.CreatedDatePascalName);
				//if (_currentTable.AllowCreateAudit) columnList.Add(_model.Database.CreatedByPascalName);
				//if (_currentTable.AllowModifiedAudit) columnList.Add(_model.Database.ModifiedByPascalName);
				//foreach (var columnName in columnList)
				//{
				//  sb.Append(columnName);
				//  if (columnList.IndexOf(columnName) < columnList.Count() - 1)
				//    sb.Append(", ");
				//}
				//sb.AppendLine();
				//sb.AppendLine("		</sql-query>");
				#endregion

				sb.AppendLine("	</class>");
				sb.AppendLine("</hibernate-mapping>");
			}
			catch (Exception ex)
			{
				throw;
			}

		}

		#endregion

	}
}